package org.hackystat.devcathlon.ui.page.register;

import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.wicket.Resource;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.WebResource;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.form.Radio;
import org.apache.wicket.markup.html.form.RadioGroup;
import org.apache.wicket.markup.html.form.StatelessForm;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.form.upload.FileUpload;
import org.apache.wicket.markup.html.form.upload.FileUploadField;
import org.apache.wicket.markup.html.form.validation.FormComponentFeedbackBorder;
import org.apache.wicket.markup.html.image.Image;
import org.apache.wicket.markup.html.image.NonCachingImage;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.util.file.File;
import org.apache.wicket.util.resource.FileResourceStream;
import org.apache.wicket.util.resource.IResourceStream;
import org.hackystat.devcathlon.engine.profile.Profile;
import org.hackystat.devcathlon.engine.profile.ProfileManager;
import org.hackystat.devcathlon.engine.user.User;
import org.hackystat.devcathlon.engine.user.UserManager;
import org.hackystat.devcathlon.ui.DevcathlonApplication;
import org.hackystat.devcathlon.ui.DevcathlonSession;
import org.hackystat.devcathlon.ui.page.form.validators.UserNameValidator;
import org.hackystat.devcathlon.ui.page.profile.ProfilePage;

/**
 * Required Account Registration form for new participants/users to 
 * Devcathlon. This will also help users get started in filling out 
 * their basic and personal information.
 * 
 * @author Anthony Du
 *
 */
public class AccountForm extends StatelessForm {
  /** Support Serialization. */
  private static final long serialVersionUID = 1L;
  /** The user instance. */
  private User user = new User();
  /** The profile instance. */
  private Profile profile = new Profile();
  /** Default file output path. */
  private static final String fileSeparator = System.getProperty("file.separator");
  /** Application's root path. */
  private static final String appRootPath = System.getProperty("user.home") + fileSeparator
  + ".hackystat" + fileSeparator + "devcathlon" + fileSeparator;
  /** Default file output path. */
  // private static final String tmpOutputPath = appRootPath + "tmp" + fileSeparator;
  /** Assigned file output path. */
  private static final String fileOutputPath =  appRootPath + "users" + fileSeparator
    + "avatars" + fileSeparator;
  /** Default avatar file name. */
  private static final String defaultPhotoName = "Dummy_user.png";
  /** Default avatar file path. */
  private static final String defaultPhotoPath = fileOutputPath + defaultPhotoName;
  /** File upload component. */
  private FileUploadField upload;
  /** Image component. */
  Image photoImage;
  /** The feedback panel for errors. */
  FeedbackPanel feedbackPanel;
  /** User name validator. */
  UserNameValidator userNameValidator = new UserNameValidator();

  /**
   * Account Registration form.
   * @param id The unique form id.
   */
  public AccountForm(String id) {
    super(id);
    // Form feedback message.
    feedbackPanel = new FeedbackPanel("feedback");
    feedbackPanel.setOutputMarkupId(true);
    add(feedbackPanel);
    // check for existing user
    User existUser = UserManager.getInstance().getUser(DevcathlonSession.get().getEmail());
    if (null != existUser) {
      user = existUser;
      userNameValidator = new UserNameValidator(user);
    }
    // Set the component property model of this form to the user instance.
    setModel(new CompoundPropertyModel(user));
    // Add components to form.
    add(new FormComponentFeedbackBorder("firstNameBorder")
          .add(new TextField("firstName")
                .setRequired(true).setLabel(new Model("Firstname"))));
    add(new FormComponentFeedbackBorder("lastNameBorder")
          .add(new TextField("lastName")
                .setRequired(true).setLabel(new Model("Lastname"))));
    add(new FormComponentFeedbackBorder("usernameBorder")
          .add(new TextField("username")
                .setRequired(true).setLabel(new Model("Username"))
                .add(userNameValidator)));
    add(new FormComponentFeedbackBorder("genderBorder")
          .add(((FormComponent) new RadioGroup("genderRadio", 
              new PropertyModel(profile, "gender"))
                .add(new Radio("maleRadio", new Model("Male")))
                .add(new Radio("femaleRadio", new Model("Female"))))
              .setRequired(true)
              .setLabel(new Model("Gender"))));
    
    // Add uploader component
    upload = new FileUploadField("upload");
    add(upload);
    // Set the appropriate photo path for user
    setUserAvatar();
    // Load the actual photo
    photoImage = new NonCachingImage("photoImg", loadPhoto());
    // Set an HTML ID so that it can be refreshed
      //    photoImage.setOutputMarkupId(true);
    // The photo image container. 
    final WebMarkupContainer photoContainer = new WebMarkupContainer("photoContainer");
    photoContainer.add(photoImage);
    // File Upload component
    add(new Button("uploadButton", new Model("Upload")) {
      /** Support Serialization. */
      private static final long serialVersionUID = 1L;
      
      @Override
      public void onSubmit() {
        // Get the file uploaded
        FileUpload fileUpload = upload.getFileUpload();
        if (fileUpload == null) {
          return;
        }
        try {
          FileOutputStream out = new FileOutputStream(getUserPhotoPath());
          out.write(fileUpload.getBytes());
          out.close();
        }
        catch (IOException e) {
          ((DevcathlonApplication)this.getApplication()).getLogger().
          info("User " + getUserEmail() + " had an error uploading a file.");
          throw new RuntimeException(e);
        }
      }
    }
    .setDefaultFormProcessing(false));
    add(photoContainer);
    
    add(new TextField("location", new PropertyModel(profile, "location")));
    add(new TextField("contact", new PropertyModel(profile, "contact")));
    add(new TextArea("bioInfo", new PropertyModel(profile, "bioInfo")));
    
    // Register button
    add(new Button("registerButton", new Model("Register")) {
      private static final long serialVersionUID = 1L;
      
      @Override
      public void onSubmit() {
        String email = DevcathlonSession.get().getEmail();
        String password = DevcathlonSession.get().getPassword();
        user.setEmail(email);
        user.setPassword(password);
        String gender = profile.getGender();
        String location = profile.getLocation();
        String contact = profile.getContact();
        String bioInfo = profile.getBioInfo();
        // This is a third check for the user's avatar. Only in rare cases
        // would a user experience a loss in their assigned avatar.
        setUserAvatar();
        String avatar = profile.getAvatar();
        // Save the user-profile.
        UserManager.getInstance().addUser(user);
        ProfileManager.getInstance().addProfile(user, gender, location, contact, bioInfo, avatar);
        // Set the user & profile session.
        DevcathlonSession.get().getProfileSession().setProfile(email);
        // Redirect page to profile page.
        ((DevcathlonApplication)this.getApplication()).getLogger().
        info("User " + user.getEmail() + " processed registration form.");
        setResponsePage(new ProfilePage());
      }
    }
    .setDefaultFormProcessing(true));
    
    // Cancel button
    add(new Button("cancelButton", new Model("Cancel")) {
      /** Support Serialization. */
      private static final long serialVersionUID = 1L;

      @Override
      public void onSubmit() {
        ((DevcathlonApplication)this.getApplication()).getLogger().
        info("User " + getUserEmail() + " canceled registration.");
        getSession().invalidate();
        setResponsePage(getApplication().getHomePage());
      }
    }
    .setDefaultFormProcessing(false));
  }
  
  /**
   * Load photo.
   * 
   * @return The resource containing the photo.
   */
  private Resource loadPhoto() {
    final Resource resource = new WebResource() {
      /** Support Serialization. */
      private static final long serialVersionUID = 1L;

      public IResourceStream getResourceStream() {
        String filename = defaultPhotoName;
        if (new File(getUserPhotoPath()).exists()) {
          filename = getUserPhotoName();
          profile.setAvatar(getUserPhotoPath());
        }
        else {
          profile.setAvatar(defaultPhotoPath);
        }
        final IResourceStream stream = new FileResourceStream(
                                       new File(fileOutputPath + filename));
        return stream;
      }
    };
    return resource;
  }
  
  /**
   * Returns the user's photo path.
   * 
   * @return The current user's photo path.
   */
  private String getUserPhotoPath() {
    return fileOutputPath + getUserPhotoName();
  }
  
  /**
   * Returns the user's photo name.
   * 
   * @return The current user's photo name.
   */
  private String getUserPhotoName() {
    return getUserEmail() + ".png";
  }
  
  /**
   * Returns the session's current user.
   * 
   * @return The current user's email.
   */
  private String getUserEmail() {
    return DevcathlonSession.get().getEmail();
  }
  
  /**
   * Sets the user's avatar.
   */
  private void setUserAvatar() {
    if (new File(getUserPhotoPath()).exists()) {
      profile.setAvatar(getUserPhotoPath());
    }
    else {
      profile.setAvatar(defaultPhotoPath);
    }
  }
}
